package com.xxx.mybaitis;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.List;
import java.util.Properties;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

/**
 * 主要完成2件事情：
 * 1. 加载db连接配置文件和sqlmapper的xml文件
 * 2. 获取sqlSession连接对象
 */
public class SqlSessionFactory {
	
	// Configuration这个对象在全局中，只能存在一个，所以将其定义为final
	private final Configuration configuration = new Configuration();
	
	// 数据库连接配置文件
	private static final String DB_CONFIG_LOCATION = "db.properties";
	
	// mapper.xml存放的位置：classpath目录下
	private static final String MAPPER_CONFIG_LOCATION = "mapper"; 

	
	public SqlSessionFactory() {
		loadDBInfo(); // 加载db.properties
		loadMapperInfo(); // 加载mapper.xml
	}
	
	/**
	 * 获取一个sqlSession
	 * @return
	 */
	public SqlSession openSession() {
		return new DefaultSqlSession(configuration);
	}

	/**
	 * 加载db.properties
	 */
	private void loadDBInfo() {
		// 从db.properties配置文件里加载连接配置信息
		InputStream is = SqlSessionFactory.class.getClassLoader().getResourceAsStream(DB_CONFIG_LOCATION);
		Properties properties = new Properties();
		try {
			properties.load(is);
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		// 从配置文件里读取具体的配置参数
		configuration.setDriver(properties.getProperty("driver"));
		configuration.setUrl(properties.getProperty("url"));
		configuration.setUsername(properties.getProperty("username"));
		configuration.setPassword(properties.getProperty("password"));
	}

	/**
	 * 加载mapper.xml
	 */
	private void loadMapperInfo() {
		URL url = SqlSessionFactory.class.getClassLoader().getResource(MAPPER_CONFIG_LOCATION);
		File file = new File(url.getFile());
		if(file.isDirectory()) {
			// 读取文件夹下所有的mapper.xml文件
			File [] mappers = file.listFiles();
			// 遍历所有的mapper.xml文件，然后加载到Configuration对象里
			for(File mapperFile : mappers) {
				loadMapper(mapperFile);
			}
		}
	}

	/**
	 * 加载mapper.xml文件（SAX解析xml文件，然后放入到configuration对象里）
	 * @param mapper
	 */
	private void loadMapper(File mapperFile) {
		SAXReader saxReader = new SAXReader();
		// 通过read方法读取一个文件，转换成Document对象
		Document document = null;
		try {
			document = saxReader.read(mapperFile);
		} catch (DocumentException e) {
			e.printStackTrace();
		}
		
		// 解析sql mapper.xml文件
		// 1.获取根节点：<mapper>
		Element rootElement = document.getRootElement();
		// 从根节点里获取namespace
		String namespace = rootElement.attribute("namespace").getData().toString();
		
		// 2.获取子节点：<select>
		List<Element> selects = rootElement.elements("select");
		// 遍历所有的<select>节点，将属性设置到MappedStatement对象里，并注册到Configuration对象中
		for(Element element : selects) {
			// id
			String id = element.attribute("id").getData().toString();
			// resultType
			String resultType = element.attribute("resultType").getData().toString();
			// sql
			String sql = element.getData().toString();
			// sourceId=namespace.id
			String sourceId = namespace + "." + id;
			// 将解析到的属性设置到MappedStatement对象中
			MappedStatement mappedStatement = new MappedStatement();
			mappedStatement.setNamespace(namespace);
			mappedStatement.setResultType(resultType);
			mappedStatement.setSourceId(sourceId);
			mappedStatement.setSql(sql);
			mappedStatement.setSqlCommandType("select");
			configuration.getMappedStatementMap().put(sourceId, mappedStatement);
		}
		
		// 3.获取子节点：<insert>
		List<Element> inserts = rootElement.elements("insert");
		// 遍历所有的<select>节点，将属性设置到MappedStatement对象里，并注册到Configuration对象中
		for(Element element : inserts) {
			// id
			String id = element.attribute("id").getData().toString();
			// sql
			String sql = element.getData().toString();
			// sourceId=namespace.id
			String sourceId = namespace + "." + id;
			// 将解析到的属性设置到MappedStatement对象中
			MappedStatement mappedStatement = new MappedStatement();
			mappedStatement.setNamespace(namespace);
			mappedStatement.setSourceId(sourceId);
			mappedStatement.setSql(sql);
			mappedStatement.setSqlCommandType("insert");
			configuration.getMappedStatementMap().put(sourceId, mappedStatement);
		}

		// 4.获取子节点：<update>
		List<Element> updates = rootElement.elements("update");
		// 遍历所有的<update>节点，将属性设置到MappedStatement对象里，并注册到Configuration对象中
		for(Element element : updates) {
			// id
			String id = element.attribute("id").getData().toString();
			// sql
			String sql = element.getData().toString();
			// sourceId=namespace.id
			String sourceId = namespace + "." + id;
			// 将解析到的属性设置到MappedStatement对象中
			MappedStatement mappedStatement = new MappedStatement();
			mappedStatement.setNamespace(namespace);
			mappedStatement.setSourceId(sourceId);
			mappedStatement.setSql(sql);
			mappedStatement.setSqlCommandType("update");
			configuration.getMappedStatementMap().put(sourceId, mappedStatement);
		}

		// 5.获取子节点：<delete>
		List<Element> deletes = rootElement.elements("delete");
		// 遍历所有的<delete>节点，将属性设置到MappedStatement对象里，并注册到Configuration对象中
		for(Element element : deletes) {
			// id
			String id = element.attribute("id").getData().toString();
			// sql
			String sql = element.getData().toString();
			// sourceId=namespace.id
			String sourceId = namespace + "." + id;
			// 将解析到的属性设置到MappedStatement对象中
			MappedStatement mappedStatement = new MappedStatement();
			mappedStatement.setNamespace(namespace);
			mappedStatement.setSourceId(sourceId);
			mappedStatement.setSql(sql);
			mappedStatement.setSqlCommandType("delete");
			configuration.getMappedStatementMap().put(sourceId, mappedStatement);
		}
	}
	
}
